3. 配置

也许在这里谈论配置对于一个“约定优于配置”的框架来说,这可能比较奇怪,但这些配置通常都是一次性,我们最好还是先了解他们的大概。

由于Grails提供了默认设置,你确实可以在不做任何配置的情况下进行开发和应用。Grails也内嵌了一个Web容器和一个称为HSQLDB的内存数据库,这意味着你甚至都不用安装数据库了。

不过,在将来某些情况下你还是会想要安装一个真正的数据库的,我们将在随后的一些章节进行描述。

3.1 基本配置

Grails提供了一个名为 grails-app/conf/Config.groovy 的文件用来进行一般性配置。这个文件使用了Groovy的 ConfigSlurper 特性,除了它是由纯正的Groovy实现外,它与Java的properties文件是非常相似的,因此你可以在应用中重用定义的变量或者使用适合的Java类型!

你可以在这里添加你自己的配置,例如:

foo.bar.hello = "world"

配置完成后你就可以在你的应用程序里使用两种方式来访问这些设置了。最常用是通过 GrailsApplication 对象,它可以在控制器或标记库中作为一个变量来使用:

assert "world" == grailsApplication.config.foo.bar.hello

另一种方式是先获得对 ConfigurationHolder 类的引用,然后再通过它获得配置对象的引用:

import org.codehaus.groovy.grails.commons.*
…
def config = ConfigurationHolder.config
assert "world" == config.foo.bar.hello

3.1.1 内置选项

Grails提供了下列配置选项:

生成War文件

要获得使用这些选项的更多信息,可以参考部署一章

3.1.2 日志

日志基础

Grails使用它的通用配置方式来配置潜在的 Log4j 日志系统。要配置日志你需要修改位于 grails-app/conf 目录下的 Config.groovy 文件。

这个独特的 Config.groovy 文件允许你为 开发(development)测试(test)生产(production)环境(environments)分别进行日志的配置。Grails将适当地处理 Config.groovy 文件并配置Log4j。

从1.1版本的Grails开始,提供了一个 Log4j DSL,你可以像如下例子一样来配置Log4j:

log4j = {
    error  'org.codehaus.groovy.grails.web.servlet',  //  controllers
               'org.codehaus.groovy.grails.web.pages' //  GSP

warn 'org.mortbay.log' }

实际上,每个方法都可以转化为一个日志级别,你可以把你想要记录日志的包名作为方法的参数。

以下是一些有用的日志记录器:

顶级日志记录器

顶级日志记录器会被所有其他日志记录器继承。你可以使用root方法来配置顶级日志记录器:

root {
    error()
    additivity = true
}

下边的例子用来配置顶级日志记录器去记录错误级别的信息,它的上方是默认的标准输出目标。你也可以将顶级日志记录器配置为将日志输出到多个已命名的输出目标:

appenders {
        file name:'file', file:'/var/logs/mylog.log'
}
root {
    debug 'stdout', 'file'
    additivity = true
}

这里的顶级日志记录器将日志记录到了两个输出目标——默认的“stdout”输出目标和一个“file”输出目标。

你也可以通过参数方式进入Lorg4J闭包的方式来配置顶级日志记录器:

log4j = { root ->
    root.level = org.apache.log4j.Level.DEBUG
    …
}
闭包参数“root”是 org.apache.log4j.Logger 的一个实例,因此你可以查阅Log4J的API文档,找出哪些属性和方法对你有用。

自定义输出目标

使用Log4j你可以明确的定义输出目标。下边是默认可用的输出目标:

例如你可以配置一个滚动文件输出目标:

log4j = {
        appenders {
                rollingFile name:"myAppender", maxFileSize:1024, fileName:"/tmp/logs/myApp.log"
        }
}

每个进入输出目标的参数都会对应到 Appender 类的一个属性。上边的例子设置了RollingFileAppender 类的namemaxFileSizefileName属性。

如果你愿意通过自己编程来创建输出目标或者你已经有自己的输出目标实现,那么你可以简单地调用 appender 方法以及输出目标实例:

import org.apache.log4j.*

log4j = { appenders { appender new RollingFileAppender(name:"myAppender", maxFileSize:1024, fileName:"/tmp/logs/myApp.log") } }

现在你可以将输出目标的名称作为一个唯一值设置到某个日志级别方法中,这样日志就记录到一个特定的输出目标中。这些在上一节讲述过:

error myAppender:"org.codehaus.groovy.grails.commons"

自定义布局

Log4j DSL默认假设你想要使用 样板布局(PatternLayout) 日志格式。也有如下其他布局可用使用:

你可以使用layout设置来指定自定义的样板作为一个输出目标:

log4j = {
        appenders {
        console name:'customAppender', layout:pattern(conversionPattern: '%c{2} %m%n')
    }
}

这样的设置也可以用于内置的“stdout”输出目标,这样会将日志输出到控制台中:

log4j = {
    appenders {
        console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
    }
}

完整的堆栈日志跟踪

当发生异常时,会产生大量来自Java和Groovy内部的堆栈日志信息。Grails过滤了那些典型的无关信息,同时聚焦到非Grails/Groovy核心类的信息上。

当这种情况发生时,完整的追踪信息总是会写到 StackTrace 日志记录器。这些日志被记录到一个称为stacktrace.log的文件中 - 当然你也可以修改 Config.groovy 文件来进行你想要的设置。例如,如果你更喜欢将完整的堆栈记录信息输出到标准输出,可以添加这样一行:

error stdout:"StackTrace"

你也可以将 grails.full.stacktrace 虚拟机属性设置为 true 来完全禁用堆栈跟踪过滤器:

grails -Dgrails.full.stacktrace=true run-app

约定的日志记录方式

所有的应用程序工件都有一个动态添加的 log 属性。这些工件类型包括 domain类控制器和标记库等。下边是一个使用例子:

def foo = "bar"
log.debug "The value of foo is $foo"

Grails使用 grails.app.<工件类型>.ClassName 来作为日志记录器的命名。下边是一个如何配置日志记录器去记录不同Grails工件的日志的例子:

log4j = {
        // 所有的应用程序工件设置
        info "grails.app"
        // 一个特定的控制器设置
        debug "grails.app.controller.YourController"
        // 一个特定的domain类设置
        debug "grails.app.domain.Book"
        // 所有的标记库设置
        info "grails.app.tagLib"

}

工件名称(<工件类型>)也是按照约定命名的,一些常见的如下列表:

3.2 环境

多环境配置

Grails支持“多环境配置”的概念。grails-app/conf中的Config.groovyDataSource.groovy两个文件能够使用ConfigSlurper提供的语法来应用“多环境配置”的特性。以下例子是Grails提供的默认 DataSource 里的定义:

dataSource {
    pooled = false                          
    driverClassName = "org.hsqldb.jdbcDriver"       
    username = "sa"
    password = ""                           
}
environments {
    development {
        dataSource {
            dbCreate = "create-drop" // 可选“create”、“createeate-drop”和“update”中的一个
            url = "jdbc:hsqldb:mem:devDB"
        }
    }   
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:mem:testDb"
        }
    }   
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:file:prodDb;shutdown=true"
        }
    }
}

注意配置文件的开头部分提供的是公共配置,紧接着的 environments 代码块则指定了用于独立环境配置的数据源信息,包括dbCreateurl属性。这样的语法也可以用与Config.groovy文件。

针对不同环境的打包和运行

Grails的命令行 已经内建了针对特定环境来执行任何命令的能力。格式为:

grails [环境名] [命令名]

另外,已经有三个Grails的预制环境:devprodtest分别用于 开发生产测试。例如要为 test 环境创建一个WAR包,你可以这样做:

grails test war

如果你有自建的其他环境需要使用,可以通过 grails.env 变量来设置并用于任何命令:

grails -Dgrails.env=UAT run-app

可编程的环境检测

在你的Gant脚本或系统启动类的代码中,你可以使用 Environment 类来检测环境:

import grails.util.Environment

...

switch(Environment.current) { case Environment.DEVELOPMENT: configureForDevelopment() break case Environment.PRODUCTION: configureForProduction() break }

3.3 数据源

Grails是基于Java技术构建的,因此要在其中安装数据源必然需要一些JDBC(这种技术并不只支持Java数据库连接)的知识。

根本上来说,如果你正在使用的另一种数据库,而不是Grails内嵌的HSQLDB,那么你就需要为它准备一个JDBC驱动。例如使用MySQL数据库,就需要 Connector/J

这个JDBC驱动。通常这些JDBC驱动都是以JAR文件格式发行的。将需要的JAR文件放到项目的 lib 目录下即可。

一旦你把JAR文件放到了正确的位置,你还需要熟悉位于 grails-app/conf/DataSource.groovy 的Grails数据库描述文件。这个文件包含了数据源的定义,其中有下列这些设置:

一个用于MySQL数据库的典型配置可以像这样:

dataSource {
        pooled = true
        dbCreate = "update"
        url = "jdbc:mysql://localhost/yourDB"
        driverClassName = "com.mysql.jdbc.Driver"
        username = "yourUser"
        password = "yourPassword"   
}

在配置数据源的时候不要在配置项之前包含类型声明或def关键字,否则Groovy会把它们当作本地变量定义并且不对它们进行处理。例如下边的例子就是无效的:

dataSource {
        boolean pooled = true // 类型声明导致它被当作是一个本地变量
        …
}

3.3.1 数据源和环境

前边的配置范例假设你想要对所有的环境做一些配置,包括:生产、测试和开发等。

Grails的数据源定义是“环境感知”的,因此你可以针对需要的环境这样配置:

dataSource {
        // 这里放置公共设置
}                     
environments {
  production {
     dataSource {
          url = "jdbc:mysql://liveip.com/liveDb"                                    
     }                     
  }
}

3.3.2 JNDI数据源

许多Java EE容器通常都支持通过 Java命名与目录接口 (JNDI)来获取 数据源 实例。有时你可能需要通过JNDI去查找一个 数据源

Grails支持像下边这样的JNDI数据源定义:

dataSource {
    jndiName = "java:comp/env/myDataSource"
}

JNDI的名称格式在不同的容器中会有不同,但是在定义 数据源 的方式上是一致的。

3.3.3 自定数据库迁移

DataSourcedbCreate 属性是非常重要的,它会指示Grails在运行期间使用 GORM 类来自动生成数据库表。选项如下:

create-dropcreate 都会删除所有存在的数据,因此请小心使用!

In 部署 模式下 dbCreate 默认被设置为“create-drop”:

dataSource {
        dbCreate = "create-drop" // one of 'create', 'create-drop','update'
}

在每次应用程序重启时都会自动删除并重建数据库表。显然,这不应该用于生产环境。

尽管目前Grails还不支持Rails风格的开箱迁移特性,但有两个插件可以提供Grails类似的简单能力:LiquiBase插件和DbMigrate插件都可以通过grails list-plugins命令获得。

3.4 外部配置

大多数情况下, grails-app/conf 目录下的 Config.groovy 默认配置文件是足够使用了,但可能有某些特殊情况让你想要在主应用程序框架 之外 维护一个配置文件。例如你使用WAR文件部署了系统,管理员会经常需要修改配置文件来改变系统的特性,但又要避免每次修改都得重新打包生成WAR文件。

为了支持这种外部配置文件的部署方案,你需要在Config.groovy文件的grails.config.locations设置中指明你的外部配置文件所在位置:

grails.config.locations = [ "classpath:${appName}-config.properties",
                            "classpath:${appName}-config.groovy",
                            "file:${userHome}/.grails/${appName}-config.properties",
                            "file:${userHome}/.grails/${appName}-config.groovy"]

上边的例子演示了从classpath和USER_HOME这些不同的位置来加载配置文件(包括Java属性(properties)文件和 ConfigSlurper 配置)。

最终所有的配置文件都被合并到了 GrailsApplication 对象的 config 属性中,就可以通过这个属性来获取配置信息了。

Grails也支持Spring中定义的属性占位(property place holder)概念和属性重载(property override)配置,更多信息请查看 Grails和Spring一章。

3.5 版本管理

版本管理基础

Grails已经内置了对版本管理的支持。当首次使用 create-app 命令创建应用程序的时候,应用程序的版本就被设置为 0.1 了。这个版本信息被记录在项目根目录下的应用程序元数据文件 application.properties 里边。

需要改变你的应用程序版本时你可以运行 set-version 命令:

grails set-version 0.2

版本信息被用在各种命令中,例如 war 命令就会将应用程序版本附加到创建的WAR文件末尾。

运行期间检测版本

你可以使用Grails对应用程序元数据的支持来检测应用程序版本,也就是使用 GrailsApplication 类。例如在 控制器 里你可以使用隐藏的 grailsApplication 变量:

def version = grailsApplication.metadata['app.version']

如果你需要获得的不是应用程序的版本而是Grails环境的版本,那么可以这样做:

def grailsVersion = grailsApplication.metadata['app.grails.version']

也可以使用 GrailsUtil 类:

import grails.util.*
def grailsVersion = GrailsUtil.grailsVersion

Show details